package cn.jetpiece.cloud.joss.sdk.core.util;

import cn.jetpiece.cloud.joss.sdk.config.properties.OssApiProperties;
import cn.jetpiece.cloud.joss.sdk.core.exception.OssExceptionEnum;
import cn.jetpiece.cloud.joss.sdk.core.exception.OssSdkException;
import cn.jetpiece.cloud.joss.sdk.core.oss.model.OssSdkBaseResult;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * @author XuJZ
 * @since 2021/1/28
 */
public class RestUtils {
    public static final RestTemplate REST_TEMPLATE = new RestTemplate();
    private final Logger log = LoggerFactory.getLogger(RestUtils.class);
    private OssApiProperties ossApiProperties;

    public void setOssApiProperties(OssApiProperties ossApiProperties) {
        this.ossApiProperties = ossApiProperties;
    }


    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, MultiValueMap<String, String> queryParams, File file, Class<T> tClass) {
        checkBaseParam();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
            httpHeaders.setConnection("Keep-Alive");
            httpHeaders.setCacheControl("no-cache");

            MultiValueMap<String, Object> bodyParams = new LinkedMultiValueMap<>();
            FileSystemResource resource = new FileSystemResource(file);
            bodyParams.add("file", resource);
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(bodyParams, httpHeaders);

            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, MultiValueMap<String, String> queryParams, InputStream inputStream, Class<T> tClass) {
        checkBaseParam();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
            MultiValueMap<String, Object> bodyParams = new LinkedMultiValueMap<>();
            Resource resource = new CommonInputStreamResource(inputStream);
            bodyParams.add("file", resource);
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(bodyParams, httpHeaders);
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, Class<T> tClass) {
        checkBaseParam();
        MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Object> entity = new HttpEntity<>(httpHeaders);
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, Object object, Class<T> tClass) {
        checkBaseParam();
        MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Object> entity = new HttpEntity<>(object, httpHeaders);
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, MultiValueMap<String, String> queryParams, String body, Class<T> tClass) {
        checkBaseParam();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.TEXT_PLAIN);
            HttpEntity<String> entity = new HttpEntity<>(body, httpHeaders);
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public <T> OssSdkBaseResult<T> exchange(String url, HttpMethod httpMethod, MultiValueMap<String, String> queryParams, Class<T> tClass) {
        checkBaseParam();
        buildSign(queryParams);
        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.TEXT_PLAIN);
            HttpEntity<Object> entity = new HttpEntity<>(httpHeaders);
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(builder.build().toUri(), httpMethod, entity, String.class);
            String response = responseEntity.getBody();
            if (response == null) {
                throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
            }
            return JSONObject.parseObject(response, new TypeReference<OssSdkBaseResult<T>>(tClass) {
            });
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    public void delete(String url, MultiValueMap<String, String> queryParams) {
        checkBaseParam();
        buildSign(queryParams);
        try {
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(getServerAddr() + url).queryParams(queryParams);
            REST_TEMPLATE.delete(builder.build().toUri());
        } catch (RestClientException e) {
            log.error(e.getMessage(), e);
            throw new OssSdkException(OssExceptionEnum.ACCESS_OSS_ERROR);
        }
    }

    /**
     * 检查调用参数
     */
    public void checkBaseParam() {
        if (ossApiProperties.getServerAddr() == null || ossApiProperties.getServerAddr().length() < 1) {
            throw new OssSdkException(404, "OSS服务地址不能为空");
        }
        if (ossApiProperties.getAccessKey() == null || ossApiProperties.getAccessKey().length() < 1) {
            throw new OssSdkException(404, "OSS公钥不能为空");
        }
        if (ossApiProperties.getSecretKey() == null || ossApiProperties.getSecretKey().length() < 1) {
            throw new OssSdkException(404, "OSS密钥不能为空");
        }
    }

    public void buildSign(MultiValueMap<String, String> queryParams) {
        long timestamp = System.currentTimeMillis();
        queryParams.add("accessKey", ossApiProperties.getAccessKey());
        queryParams.add("timestamp", timestamp + "");
        String signStr = ossApiProperties.getSecretKey() + ossApiProperties.getAccessKey() + timestamp + ossApiProperties.getSecretKey();
        queryParams.add("sign", DigestUtils.md5DigestAsHex(signStr.getBytes(StandardCharsets.UTF_8)));
    }

    public String getServerAddr() {
        if (ossApiProperties.getServerAddr().endsWith("/joss")) {
            return ossApiProperties.getServerAddr().substring(0, ossApiProperties.getServerAddr().length() - 5);
        } else if (ossApiProperties.getServerAddr().endsWith("/oss")) {
            return ossApiProperties.getServerAddr().substring(0, ossApiProperties.getServerAddr().length() - 4);
        }
        return ossApiProperties.getServerAddr();
    }
}
